﻿using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;
using System.Collections.Specialized;

namespace Shapefile
{
    class ShapeFileEnumerator : IEnumerator<ESRIShape>
    {
        private OleDbCommand dbCommand_;
        private OleDbDataReader dbReader_;
        private int currentIndex_ = -1;
        private bool rawMetadataOnly_;
        private FileStream mainStream_;
        private FileStream indexStream_;
        private int count_;

        public ShapeFileEnumerator(OleDbConnection dbConnection, string selectString, bool rawMetadataOnly, FileStream mainStream,
                                   FileStream indexStream, int count)
        {

            rawMetadataOnly_ = rawMetadataOnly;
            mainStream_ = mainStream;
            indexStream_ = indexStream;
            count_ = count;
            dbCommand_ = new OleDbCommand(selectString, dbConnection);
            dbReader_ = dbCommand_.ExecuteReader();
        }

        #region IEnumerator<ESRIShape> Members

        public ESRIShape Current
        {
            get
            {
                // get the metadata
                StringDictionary metadata = null;
                if (!rawMetadataOnly_)
                {
                    metadata = new StringDictionary();
                    for (int i = 0; i < dbReader_.FieldCount; i++)
                    {
                        metadata.Add(dbReader_.GetName(i),
                            dbReader_.GetValue(i).ToString());
                    }
                }

                // get the index record
                byte[] indexHeaderBytes = new byte[8];
                indexStream_.Seek(ESRIHeader.HeaderLength + currentIndex_ * 8, SeekOrigin.Begin);
                indexStream_.Read(indexHeaderBytes, 0, indexHeaderBytes.Length);
                int contentOffsetInWords = BinaryConverter.ToInt32(indexHeaderBytes, 0, ByteOrder.Big);
                int contentLengthInWords = BinaryConverter.ToInt32(indexHeaderBytes, 4, ByteOrder.Big);

                // get the data chunk from the main file - need to factor in 8 byte record header
                int bytesToRead = (contentLengthInWords * 2) + 8;
                byte[] shapeData = new byte[bytesToRead];
                mainStream_.Seek(contentOffsetInWords * 2, SeekOrigin.Begin);
                mainStream_.Read(shapeData, 0, bytesToRead);

                return ShapeFactory.ParseShape(shapeData, metadata, dbReader_);
            }
        }

        #endregion

        #region IEnumerator Members

        object System.Collections.IEnumerator.Current
        {
            get
            {
                return this.Current;
            }
        }

        public void Dispose()
        {
            dbReader_.Close();
            dbCommand_.Dispose();
        }

        public bool MoveNext()
        {

            if (currentIndex_++ < (count_ - 1))
            {
                // 尝试读取下一条记录
                if (!dbReader_.Read())
                {
                    throw new InvalidOperationException("Metadata database does not contain a record for the next shape");
                }

                return true;
            }
            else
            {
                // 达到最后一条记录
                return false;
            }
        }


        public void Reset()
        {
            dbReader_.Close();
            dbReader_ = dbCommand_.ExecuteReader();
            currentIndex_ = -1;
        }

        #endregion

    }
}
